2020-06-25 14:29:15
Author: Cedric Huchuan Xia (email, github)
Affiliation: Penn Lifespan Informatics and Neuroimaging Center (PennLINC)
1. Setup Environment
require(reshape2)
require(sna)
require(tidyr)
project_path = "~/Documents/xia_gps/"
data_path = file.path(project_path,"data/flywheel_data/network_txt")
2. Setup Target and Database FC
atlases = c("HarvardOxford","desikanKilliany","aal116","schaefer200x7","power264","gordon333","glasser360","schaefer400x7")
subj_net_files_1 = list()
subj_net_files_2 = list()
for (atlas in atlases) {
subjects = list.files(file.path(data_path))
for (subj in subjects) {
subj_files = list.files(file.path(data_path,subj))
net_pattern_1 = paste0("*task-rest*single*",atlas,"*")
net_pattern_2 = paste0("*task-rest*multi*",atlas,"*")
file_path_1 = file.path(data_path,
subj,subj_files[grep(glob2rx(net_pattern_1),subj_files)])
file_path_2 = file.path(data_path,
subj,subj_files[grep(glob2rx(net_pattern_2),subj_files)])
if (length(file_path_1)>0) {subj_net_files_1[[atlas]][[subj]] = read.table(file_path_1) }
if (length(file_path_2)>0) {subj_net_files_2[[atlas]][[subj]] = read.table(file_path_2) }
}
}
Visutalize FC matrix
matrix_fc = matrix(NA, 264,264)
matrix_fc[lower.tri(matrix_fc, diag = F)] = subj_net_files_1$power264[[1]][,1]
matrix_fc = symmetrize(matrix_fc, rule = "lower")
levelplot(matrix_fc,scales=list(draw=FALSE),col.regions = rev(rainbow(80))[-c(1:5)], region =T, ylab.right = "Pearson correlation", main=list(label='Singleband FC'),xlab="",ylab="")

4. Create Subj Similarity Matrix
gps_wide_matrix = data.frame()
for (subj in arrange(subj_days,`Days Collected`)$IID){
#if ((subj %in% subj_days$IID[which(subj_days$`Days Collected`<=10)]) == T) {
for (part in 1:1){
half_1 = gps_clean2_feature$subj_mat_1[[subj]][[part]]$cor
half_2 = gps_clean2_feature$subj_mat_2[[subj]][[part]]$cor
half_1_half_2 = c(half_1,half_2)
gps_wide_matrix = rbind(gps_wide_matrix,half_1)
gps_wide_matrix = rbind(gps_wide_matrix,half_2)
}
#}
}
gps_wide_matrix = t(gps_wide_matrix)
gps_corplot = rquery.cormat(gps_wide_matrix, type = "full",graph=FALSE)
gps_corplot$subj = arrange(subj_days,`Days Collected`)$IID
levelplot(gps_corplot$r,scales=list(draw=FALSE),col.regions = rev(rainbow(1000))[-c(1:20)], region =T, ylab.right = "Pearson correlation", main=list(label='GPS Feature Similarity'),xlab="",ylab="")


fc_corplot$schaefer400x7$plot

fc_corplot$schaefer200x7$plot

fc_corplot$power264$plot

5. Create FC Atlas Similarity Matrix
fc_cor_r = lapply(fc_corplot, function(atlas) atlas$sim_mat$r[lower.tri(atlas$sim_mat$r)])
fc_cor_r_cor = lapply(fc_cor_r, function(r1) sapply(fc_cor_r, function(r2) cor.test(r1,r2)))
fc_cor_r_cor_mat = sapply(fc_cor_r_cor, function(atlas) atlas['estimate',])
levelplot(fc_cor_r_cor_mat,col.regions = rev(rainbow(100))[-c(1:20)],
region =T, ylab.right = "Pearson correlation",
main=list(label=paste('FC Atlas Similarity Matrix')), xlab="",ylab="",
scales=list(x=list(at = 1:length(atlases), labels=atlases,rot=90, tck = 0),
y=list(at = 1:length(atlases),labels=atlases, tck = 0)))

6. Compute Whole Brain FC-GPS Similarity Matrix Correlations
wbFC_gps_cor = unlist(sapply(fc_cor_r, function(atlas) cor.test(atlas, gps_corplot$r[lower.tri(gps_corplot$r)]))[c('estimate'),])
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
3: In readChar(file, size, TRUE) : truncating string with embedded nuls
4: In readChar(file, size, TRUE) : truncating string with embedded nuls
5: In readChar(file, size, TRUE) : truncating string with embedded nuls
6: In readChar(file, size, TRUE) : truncating string with embedded nuls
7: In readChar(file, size, TRUE) : truncating string with embedded nuls
8: In readChar(file, size, TRUE) : truncating string with embedded nuls
9: In readChar(file, size, TRUE) : truncating string with embedded nuls
wbFC_gps_cor_df = data_frame(atlas = gsub("\\..*","",names(wbFC_gps_cor)), corr = wbFC_gps_cor)
p = ggplot(data=wbFC_gps_cor_df, aes(x=atlas, y=corr)) +
geom_bar(stat="identity") + scale_y_continuous(limits=c(-0.5,0.5)) +
theme_cowplot() + theme(axis.text.x = element_text(angle = 45, vjust = 0.5)) +
ylab("FC-GPS Similarity Matrices Correlatons") + xlab("")
p

7. Match FC target to database

8. Permutation Test for Matching
subj_net_perm_1 = get_subj_net_perm(subj_net_files_1)
Error in sample(length(subj_net_files[[atalas]])) :
object 'atalas' not found
data_long <- gather(perm_acc, key = "atlas", value = "accuracy", atlases)
Note: Using an external vector in selections is ambiguous.
[34mℹ[39m Use `all_of(atlases)` instead of `atlases` to silence this message.
[34mℹ[39m See <https://tidyselect.r-lib.org/reference/faq-external-vector.html>.
[90mThis message is displayed once per session.[39m

get_com_fc_accuracy = function(atlas_now){
com_nums = read.table(file.path(atlas_path,atlas_now,paste0(atlas_now,"CommunityAffiliation.1D")))$V1
com_names = read.table(file.path(atlas_path,atlas_now,paste0(atlas_now,"CommunityNames.txt")))$V1
com_names = substr(com_names,0,3)
if (atlas_now == "power264") {com_names[c(1,2)] = c("smH","smM")}
fc_com_1 = get_fc_com(com_nums, com_names, atlas_now, subj_net_files_1)
fc_com_2 = get_fc_com(com_nums, com_names, atlas_now, subj_net_files_2)
#fc_com_1_sig = fc_com_1[names(which(fc_schaef400_7_com_gps$fc_gps_df$sig == T))]
#fc_com_2_sig = fc_com_2[names(which(fc_schaef400_7_com_gps$fc_gps_df$sig == T))]
single_multi_acc_com = get_acc_finger(fc_com_1,fc_com_2)
single_multi_acc_com_df = data.frame(atlas = names(single_multi_acc_com$acc), accuracy = single_multi_acc_com$acc, FC_GPS_sig = fc_schaef400_7_com_gps$fc_gps_df$sig == T)
p = ggplot(data=single_multi_acc_com_df, aes(x=reorder(atlas, accuracy), y=accuracy, fill = FC_GPS_sig)) +
geom_bar(stat="identity") +
theme_cowplot() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5)) +
ylab("FC Fingerprint Accuracy") + xlab("")
return(list(df = single_multi_acc_com_df, plot = p))
}
s400_fc_com_acc$plot / s200_fc_com_acc$plot

atlas_now
[1] "schaefer400x7"
LS0tCnRpdGxlOiAiRnVuY3Rpb25hbCBDb25uZWN0aXZpdHkgRmluZ2VycHJpbnRpbmciCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgaW5jbHVkZXM6CiAgICAgIGFmdGVyX2JvZHk6IGZvb3Rlci5odG1sCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OgogICAgICB0b2NfY29sbGFwc2VkOiB5ZXMKLS0tCmByIFN5cy50aW1lKClgCgpBdXRob3I6IFtDZWRyaWMgSHVjaHVhbiBYaWFdKGh0dHBzOi8vd3d3LnBlbm5saW5jLmlvL3RlYW0vQ2VkcmljLUh1Y2h1YW4tWGlhKSAoW2VtYWlsXShoeGlhQHVwZW5uLmVkdSksIFtnaXRodWJdKGh0dHBzOi8vZ2l0aHViLmNvbS9jZWRyaWN4LykpCgpBZmZpbGlhdGlvbjogUGVubiBMaWZlc3BhbiBJbmZvcm1hdGljcyBhbmQgTmV1cm9pbWFnaW5nIENlbnRlciAoW1Blbm5MSU5DXShwZW5ubGluYy5pbykpIAoKKioqCgojIyMgMS4gU2V0dXAgRW52aXJvbm1lbnQgCmBgYHtyIGxvYWQgbGlicmFyaWVzLCBtZXNzYWdlPUZBTFNFfQpyZXF1aXJlKHJlc2hhcGUyKQpyZXF1aXJlKHNuYSkKcmVxdWlyZSh0aWR5cikKYGBgCgpgYGB7ciBkZWZpbmUgcGF0aHN9CnByb2plY3RfcGF0aCA9ICJ+L0RvY3VtZW50cy94aWFfZ3BzLyIKZGF0YV9wYXRoID0gZmlsZS5wYXRoKHByb2plY3RfcGF0aCwiZGF0YS9mbHl3aGVlbF9kYXRhL25ldHdvcmtfdHh0IikKCmBgYAoKIyMjIDIuIFNldHVwIFRhcmdldCBhbmQgRGF0YWJhc2UgRkMKYGBge3J9CgphdGxhc2VzID0gYygiSGFydmFyZE94Zm9yZCIsImRlc2lrYW5LaWxsaWFueSIsImFhbDExNiIsInNjaGFlZmVyMjAweDciLCJwb3dlcjI2NCIsImdvcmRvbjMzMyIsImdsYXNzZXIzNjAiLCJzY2hhZWZlcjQwMHg3IikKc3Vial9uZXRfZmlsZXNfMSA9IGxpc3QoKQpzdWJqX25ldF9maWxlc18yID0gbGlzdCgpCgpmb3IgKGF0bGFzIGluIGF0bGFzZXMpIHsKICBzdWJqZWN0cyA9IGxpc3QuZmlsZXMoZmlsZS5wYXRoKGRhdGFfcGF0aCkpCiAgZm9yIChzdWJqIGluIHN1YmplY3RzKSB7CiAgICBzdWJqX2ZpbGVzID0gbGlzdC5maWxlcyhmaWxlLnBhdGgoZGF0YV9wYXRoLHN1YmopKQogICAgbmV0X3BhdHRlcm5fMSA9IHBhc3RlMCgiKnRhc2stcmVzdCpzaW5nbGUqIixhdGxhcywiKiIpCiAgICBuZXRfcGF0dGVybl8yID0gcGFzdGUwKCIqdGFzay1yZXN0Km11bHRpKiIsYXRsYXMsIioiKQogICAgCiAgICBmaWxlX3BhdGhfMSA9IGZpbGUucGF0aChkYXRhX3BhdGgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ViaixzdWJqX2ZpbGVzW2dyZXAoZ2xvYjJyeChuZXRfcGF0dGVybl8xKSxzdWJqX2ZpbGVzKV0pCiAgICBmaWxlX3BhdGhfMiA9IGZpbGUucGF0aChkYXRhX3BhdGgsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ViaixzdWJqX2ZpbGVzW2dyZXAoZ2xvYjJyeChuZXRfcGF0dGVybl8yKSxzdWJqX2ZpbGVzKV0pCiAgICAKICAgIGlmIChsZW5ndGgoZmlsZV9wYXRoXzEpPjApIHtzdWJqX25ldF9maWxlc18xW1thdGxhc11dW1tzdWJqXV0gPSByZWFkLnRhYmxlKGZpbGVfcGF0aF8xKSB9CiAgICBpZiAobGVuZ3RoKGZpbGVfcGF0aF8yKT4wKSB7c3Vial9uZXRfZmlsZXNfMltbYXRsYXNdXVtbc3Vial1dID0gcmVhZC50YWJsZShmaWxlX3BhdGhfMikgfQogIH0KfQpgYGAKCgojIyMjIFZpc3V0YWxpemUgRkMgbWF0cml4CmBgYHtyIH0KbWF0cml4X2ZjID0gbWF0cml4KE5BLCAyNjQsMjY0KQptYXRyaXhfZmNbbG93ZXIudHJpKG1hdHJpeF9mYywgZGlhZyA9IEYpXSA9IHN1YmpfbmV0X2ZpbGVzXzEkcG93ZXIyNjRbWzFdXVssMV0KbWF0cml4X2ZjID0gc3ltbWV0cml6ZShtYXRyaXhfZmMsIHJ1bGUgPSAibG93ZXIiKQpsZXZlbHBsb3QobWF0cml4X2ZjLHNjYWxlcz1saXN0KGRyYXc9RkFMU0UpLGNvbC5yZWdpb25zID0gcmV2KHJhaW5ib3coODApKVstYygxOjUpXSwgcmVnaW9uID1ULCB5bGFiLnJpZ2h0ID0gIlBlYXJzb24gY29ycmVsYXRpb24iLCBtYWluPWxpc3QobGFiZWw9J1NpbmdsZWJhbmQgRkMnKSx4bGFiPSIiLHlsYWI9IiIpCmBgYAoKCgoKIyMjIDQuIENyZWF0ZSBTdWJqIFNpbWlsYXJpdHkgTWF0cml4CmBgYHtyIEdQUyBzaW1pbGFyaXR5IG1hdHJpeH0KZ3BzX3dpZGVfbWF0cml4ID0gZGF0YS5mcmFtZSgpCgpmb3IgKHN1YmogaW4gYXJyYW5nZShzdWJqX2RheXMsYERheXMgQ29sbGVjdGVkYCkkSUlEKXsKICAjaWYgKChzdWJqICVpbiUgc3Vial9kYXlzJElJRFt3aGljaChzdWJqX2RheXMkYERheXMgQ29sbGVjdGVkYDw9MTApXSkgPT0gVCkgewogICAgZm9yIChwYXJ0IGluIDE6MSl7CiAgICAgIGhhbGZfMSA9IGdwc19jbGVhbjJfZmVhdHVyZSRzdWJqX21hdF8xW1tzdWJqXV1bW3BhcnRdXSRjb3IKICAgICAgaGFsZl8yID0gZ3BzX2NsZWFuMl9mZWF0dXJlJHN1YmpfbWF0XzJbW3N1YmpdXVtbcGFydF1dJGNvcgogICAgICBoYWxmXzFfaGFsZl8yID0gYyhoYWxmXzEsaGFsZl8yKQogICAgICBncHNfd2lkZV9tYXRyaXggPSByYmluZChncHNfd2lkZV9tYXRyaXgsaGFsZl8xKQogICAgICBncHNfd2lkZV9tYXRyaXggPSByYmluZChncHNfd2lkZV9tYXRyaXgsaGFsZl8yKQogICAgICB9CiAgICAjfQp9CgpncHNfd2lkZV9tYXRyaXggPSB0KGdwc193aWRlX21hdHJpeCkKZ3BzX2NvcnBsb3QgPSBycXVlcnkuY29ybWF0KGdwc193aWRlX21hdHJpeCwgdHlwZSA9ICJmdWxsIixncmFwaD1GQUxTRSkKZ3BzX2NvcnBsb3Qkc3ViaiA9IGFycmFuZ2Uoc3Vial9kYXlzLGBEYXlzIENvbGxlY3RlZGApJElJRAoKbGV2ZWxwbG90KGdwc19jb3JwbG90JHIsc2NhbGVzPWxpc3QoZHJhdz1GQUxTRSksY29sLnJlZ2lvbnMgPSByZXYocmFpbmJvdygxMDAwKSlbLWMoMToyMCldLCByZWdpb24gPVQsIHlsYWIucmlnaHQgPSAiUGVhcnNvbiBjb3JyZWxhdGlvbiIsIG1haW49bGlzdChsYWJlbD0nR1BTIEZlYXR1cmUgU2ltaWxhcml0eScpLHhsYWI9IiIseWxhYj0iIikKYGBgCgpgYGB7ciBGQyBjYWxjdWxhdGUgYmV0d2Vlbi1hdGxhcyBzaW1pbGFyaXR5IG1hdHJpeH0KCnNpbV9wbG90cyA9IGZ1bmN0aW9uKHN1Ympfc2VxLCBpZHMsYXRsYXNlcywgc3Vial9uZXRfZmlsZXNfMSwgc3Vial9uZXRfZmlsZXNfMil7CiAgZmNfd2lkZW1hdHJpeCA9IGxpc3QoKQogIGZjX2NvcnBsb3QgPSBsaXN0KCkKICBmb3IgKGF0bGFzIGluIGF0bGFzZXMpewogICAgcHJpbnQocGFzdGUoImF0bGFzOiIsIGF0bGFzKSkKICAgICAgZm9yIChzdWJqIGluIHN1Ympfc2VxKXsKICAgICAgICAgICNwcmludChzdWJqKQogICAgICAgICAgc3Vial9pZCA9IGFzLmNoYXJhY3RlcihpZHNbaWRzJGJlaXdlSUQgPT0gc3ViaiwnQkJMSUQnXSkKICAgICAgICAgICNwcmludChzdWJqX2lkKQogICAgICAgICAgZmNfMSA9IHVubGlzdChzdWJqX25ldF9maWxlc18xW1thdGxhc11dW1tzdWJqX2lkXV0kVjEpCiAgICAgICAgICBmY18yID0gdW5saXN0KHN1YmpfbmV0X2ZpbGVzXzJbW2F0bGFzXV1bW3N1YmpfaWRdXSRWMSkKICAgICAgICAgIAogICAgICAgICAgZmNfd2lkZW1hdHJpeFtbYXRsYXNdXSA9IGNiaW5kKGZjX3dpZGVtYXRyaXhbW2F0bGFzXV0sZmNfMSkKICAgICAgICAgIGZjX3dpZGVtYXRyaXhbW2F0bGFzXV0gPSBjYmluZChmY193aWRlbWF0cml4W1thdGxhc11dLGZjXzIpCiAgICAgICAgICAjcHJpbnQoZGltKGZjX3dpZGVtYXRyaXhbW2F0bGFzXV0pWzJdKQogICAgICB9CiAgICAgIHByaW50KGRpbShmY193aWRlbWF0cml4W1thdGxhc11dKVsyXSkKICAgICAgZmNfY29ycGxvdFtbYXRsYXNdXSRzaW1fbWF0ID0gcnF1ZXJ5LmNvcm1hdChmY193aWRlbWF0cml4W1thdGxhc11dLCB0eXBlID0gImZ1bGwiLGdyYXBoPUZBTFNFKQogICAgICBmY19jb3JwbG90W1thdGxhc11dJHN1YmogPSBzdWJqX3NlcQogICAgICBmY19jb3JwbG90W1thdGxhc11dJHBsb3QgPSBsZXZlbHBsb3QoZmNfY29ycGxvdFtbYXRsYXNdXSRzaW1fbWF0JHIsc2NhbGVzPWxpc3QoZHJhdz1GQUxTRSksY29sLnJlZ2lvbnMgPSByZXYocmFpbmJvdygxMDApKVstYygxOjIwKV0sIHJlZ2lvbiA9VCwgeWxhYi5yaWdodCA9ICJQZWFyc29uIGNvcnJlbGF0aW9uIiwgbWFpbj1saXN0KGxhYmVsPXBhc3RlKCdGQyBTaW1pbGFyaXR5IE1hdHJpeCBcbiAnLCBhdGxhcykpLHhsYWI9IlN1YmplY3RzIix5bGFiPSJTdWJqZWN0cyIpCiAgfQogIHJldHVybihmY19jb3JwbG90KQp9CgpzdWJqX3NlcSA9IGdwc19jb3JwbG90JHN1YmoKZmNfY29ycGxvdCA9IHNpbV9wbG90cyhzdWJqX3NlcSwgaWRzLCBhdGxhc2VzLCBzdWJqX25ldF9maWxlc18xLCBzdWJqX25ldF9maWxlc18yKQpgYGAKYGBge3J9CmZjX2NvcnBsb3Qkc2NoYWVmZXI0MDB4NyRwbG90CmBgYAoKYGBge3J9CmZjX2NvcnBsb3Qkc2NoYWVmZXIyMDB4NyRwbG90CmBgYAoKYGBge3J9CmZjX2NvcnBsb3QkcG93ZXIyNjQkcGxvdApgYGAKCgojIyMgNS4gQ3JlYXRlIEZDIEF0bGFzIFNpbWlsYXJpdHkgTWF0cml4CgpgYGB7ciBjYWxjdWxhdGUgc2ltaWxpYXJ5IG1hdHJpeCBiZXR3ZWVuIGF0bGFzZXN9CmZjX2Nvcl9yID0gbGFwcGx5KGZjX2NvcnBsb3QsIGZ1bmN0aW9uKGF0bGFzKSBhdGxhcyRzaW1fbWF0JHJbbG93ZXIudHJpKGF0bGFzJHNpbV9tYXQkcildKQpmY19jb3Jfcl9jb3IgPSBsYXBwbHkoZmNfY29yX3IsIGZ1bmN0aW9uKHIxKSBzYXBwbHkoZmNfY29yX3IsIGZ1bmN0aW9uKHIyKSBjb3IudGVzdChyMSxyMikpKQpmY19jb3Jfcl9jb3JfbWF0ID0gc2FwcGx5KGZjX2Nvcl9yX2NvciwgZnVuY3Rpb24oYXRsYXMpIGF0bGFzWydlc3RpbWF0ZScsXSkKCmxldmVscGxvdChmY19jb3Jfcl9jb3JfbWF0LGNvbC5yZWdpb25zID0gcmV2KHJhaW5ib3coMTAwKSlbLWMoMToyMCldLCAKICAgICAgICAgIHJlZ2lvbiA9VCwgeWxhYi5yaWdodCA9ICJQZWFyc29uIGNvcnJlbGF0aW9uIiwgCiAgICAgICAgICBtYWluPWxpc3QobGFiZWw9cGFzdGUoJ0ZDIEF0bGFzIFNpbWlsYXJpdHkgTWF0cml4JykpLCB4bGFiPSIiLHlsYWI9IiIsIAogICAgICAgICAgc2NhbGVzPWxpc3QoeD1saXN0KGF0ID0gMTpsZW5ndGgoYXRsYXNlcyksIGxhYmVscz1hdGxhc2VzLHJvdD05MCwgdGNrID0gMCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHk9bGlzdChhdCA9IDE6bGVuZ3RoKGF0bGFzZXMpLGxhYmVscz1hdGxhc2VzLCB0Y2sgPSAwKSkpCgpgYGAKIyMjIDYuIENvbXB1dGUgV2hvbGUgQnJhaW4gRkMtR1BTIFNpbWlsYXJpdHkgTWF0cml4IENvcnJlbGF0aW9ucwoKYGBge3IgY29tcGFyZSBGQyBhbmQgR1BTIHNpbWlsYXJpdHkgbWF0cml4fQp3YkZDX2dwc19jb3IgPSB1bmxpc3Qoc2FwcGx5KGZjX2Nvcl9yLCBmdW5jdGlvbihhdGxhcykgY29yLnRlc3QoYXRsYXMsIGdwc19jb3JwbG90JHJbbG93ZXIudHJpKGdwc19jb3JwbG90JHIpXSkpW2MoJ2VzdGltYXRlJyksXSkKd2JGQ19ncHNfY29yX2RmID0gZGF0YV9mcmFtZShhdGxhcyA9IGdzdWIoIlxcLi4qIiwiIixuYW1lcyh3YkZDX2dwc19jb3IpKSwgY29yciA9IHdiRkNfZ3BzX2NvcikKcCA9IGdncGxvdChkYXRhPXdiRkNfZ3BzX2Nvcl9kZiwgYWVzKHg9YXRsYXMsIHk9Y29ycikpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKC0wLjUsMC41KSkgKwogIHRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMC41KSkgKwogIHlsYWIoIkZDLUdQUyBTaW1pbGFyaXR5IE1hdHJpY2VzIENvcnJlbGF0b25zIikgKyB4bGFiKCIiKQpwCmBgYAoKIyMjIDcuIENvbXB1dGUgQ29tbXVuaXR5LUxldmVsIEZDLUdQUyBTaW1pbGFyaXR5IE1hdHJpeCBDb3JyZWxhdGlvbnMKCgpgYGB7ciBoZWxwZXIgZnVuY3Rpb25zIGZvciBnZXQgc3BlY2lmaWMgcGFydCBvZiBicmFpbn0KdmVjdG9yX3RvX21hdCA9IGZ1bmN0aW9uKGZjX3ZlY3RvciwgbWV0aG9kID0gImhhbGYiKXsKICAKICBpZiAobWV0aG9kID09ICJmdWxsIil7CiAgICBudW1fbm9kZSA9IHNxcnQobGVuZ3RoKGZjX3ZlY3RvcikpCiAgICBtYXRyaXhfZmMgPSBtYXRyaXgoTkEsIG51bV9ub2RlLCBudW1fbm9kZSkKICAgIG1hdHJpeF9mYyA9IG1hdHJpeChmY192ZWN0b3IsIG51bV9ub2RlLCBudW1fbm9kZSkKICB9IGVsc2UgaWYgKG1ldGhvZCA9PSAiaGFsZiIpewogICAgbnVtX25vZGUgPSBjZWlsaW5nKHNxcnQobGVuZ3RoKGZjX3ZlY3RvcikqMikpCiAgICBtYXRyaXhfZmMgPSBtYXRyaXgoTkEsIG51bV9ub2RlLCBudW1fbm9kZSkKICAgIG1hdHJpeF9mY1tsb3dlci50cmkobWF0cml4X2ZjLCBkaWFnID0gRildID0gZmNfdmVjdG9yCiAgfSBlbHNlIGlmIChtZXRob2QgPT0gImhhbGYrZCIpIHsKICAgIG51bV9ub2RlID0gZmxvb3Ioc3FydChsZW5ndGgoZmNfdmVjdG9yKSoyKSkKICAgIG1hdHJpeF9mYyA9IG1hdHJpeChOQSwgbnVtX25vZGUsIG51bV9ub2RlKQogICAgbWF0cml4X2ZjW2xvd2VyLnRyaShtYXRyaXhfZmMsIGRpYWcgPSBUKV0gPSBmY192ZWN0b3IKICB9CiAgCiAgbWF0cml4X2ZjID0gc3ltbWV0cml6ZShtYXRyaXhfZmMsIHJ1bGUgPSAibG93ZXIiKQogIHJldHVybihtYXRyaXhfZmMpCn0KCmdldF9mY19jb20gPSBmdW5jdGlvbihhdGxhc19ub3csIHN1YmpfbmV0KSB7CiAgY29tX251bXMgPSByZWFkLnRhYmxlKGZpbGUucGF0aChhdGxhc19wYXRoLGF0bGFzX25vdyxwYXN0ZTAoYXRsYXNfbm93LCJDb21tdW5pdHlBZmZpbGlhdGlvbi4xRCIpKSkkVjEKICBjb21fbmFtZXMgPSByZWFkLnRhYmxlKGZpbGUucGF0aChhdGxhc19wYXRoLGF0bGFzX25vdyxwYXN0ZTAoYXRsYXNfbm93LCJDb21tdW5pdHlOYW1lcy50eHQiKSkpJFYxCiAgY29tX25hbWVzID0gc3Vic3RyKGNvbV9uYW1lcywwLDMpCiAgaWYgKGF0bGFzX25vdyA9PSAicG93ZXIyNjQiKSB7Y29tX25hbWVzW2MoMSwyKV0gPSBjKCJzbUgiLCJzbU0iKX0KICBmY19tYXRfbm93X2FsbCA9IGxpc3QoKQogIGNvbV9uYW1lX3ZlYyA9IGMoKQogIGZvciAobmV0XzEgaW4gMTptYXgoY29tX251bXMpKXsKICAgIG5vZGVfbm93XzEgPSB3aGljaChjb21fbnVtcyA9PSBuZXRfMSkKICAgIG5ldF9uYW1lXzEgPSBjb21fbmFtZXNbbmV0XzFdCiAgICBmb3IgKG5ldF8yIGluIDE6bWF4KGNvbV9udW1zKSl7CiAgICAgIG5vZGVfbm93XzIgPSB3aGljaChjb21fbnVtcyA9PSBuZXRfMikKICAgICAgbmV0X25hbWVfMiA9IGNvbV9uYW1lc1tuZXRfMl0KICAgICAgbmV0d29ya19ub3cgPSBwYXN0ZShuZXRfbmFtZV8xLG5ldF9uYW1lXzIsc2VwPSItIikKICAgICAgY29tX25hbWVfdmVjID0gYyhjb21fbmFtZV92ZWMsbmV0d29ya19ub3cpCiAgICAgIGZvciAoc3ViaiBpbiBzdWJqZWN0cykgewogICAgICBmY19tYXQgPSB2ZWN0b3JfdG9fbWF0KHN1YmpfbmV0W1thdGxhc19ub3ddXVtbc3Vial1dJFYxKQogICAgICBmY19tYXRfbm93ID0gZmNfbWF0W25vZGVfbm93XzEsbm9kZV9ub3dfMl0KICAgICAgZmNfbWF0X25vdyA9IGZjX21hdF9ub3dbbG93ZXIudHJpKGZjX21hdF9ub3cpXQogICAgICBmY19tYXRfbm93X2FsbFtbbmV0d29ya19ub3ddXVtbc3Vial1dJFYxID0gYXMudmVjdG9yKGZjX21hdF9ub3cpCiAgICAgIH0KICAgIH0KICB9CiAgY29tX25hbWVfbWF0ID0gdmVjdG9yX3RvX21hdChjb21fbmFtZV92ZWMsICJmdWxsIikKICBjb21fbmFtZV92ZWNfc2hvcnQgPSBjb21fbmFtZV9tYXRbbG93ZXIudHJpKGNvbV9uYW1lX21hdCwgZGlhZyA9IFQpXQogIGZjX21hdF9ub3dfYWxsX3Nob3J0ID0gZmNfbWF0X25vd19hbGxbbmFtZXMoZmNfbWF0X25vd19hbGwpICVpbiUgY29tX25hbWVfdmVjX3Nob3J0XSAgIAogIHJldHVybihmY19tYXRfbm93X2FsbF9zaG9ydCkKfQoKCmZjX2NvbV9ncHMgPSBmdW5jdGlvbihzdWJqX25ldF9maWxlc18xLHN1YmpfbmV0X2ZpbGVzXzIsYXRsYXNfbm93KXsKICBhdGxhc19wYXRoID0gIi9Vc2Vycy9oeGlhL0RvY3VtZW50cy9HaXRIdWIveGNwRW5naW5lL2F0bGFzIgogIGZjX2NvbV8xID0gZ2V0X2ZjX2NvbShhdGxhc19ub3csIHN1YmpfbmV0ID0gc3Vial9uZXRfZmlsZXNfMSkKICBmY19jb21fMiA9IGdldF9mY19jb20oYXRsYXNfbm93LCBzdWJqX25ldCA9IHN1YmpfbmV0X2ZpbGVzXzIpCiAgZmNfY29tX2NvcnBsb3QgPSBzaW1fcGxvdHMoc3Vial9zZXEsIGlkcyxuYW1lcyhmY19jb21fMSkgLCBmY19jb21fMSwgZmNfY29tXzIpCiAgZmNfY29tX2Nvcl9yID0gbGFwcGx5KGZjX2NvbV9jb3JwbG90LCBmdW5jdGlvbihhdGxhcykgYXRsYXMkc2ltX21hdCRyW2xvd2VyLnRyaShhdGxhcyRzaW1fbWF0JHIpXSkKICBmY19jb21fZ3BzX3B2YWx1ZSA9IHVubGlzdChzYXBwbHkoZmNfY29tX2Nvcl9yLCBmdW5jdGlvbihhdGxhcykgY29yLnRlc3QoYXRsYXMsIGdwc19jb3JwbG90JHJbbG93ZXIudHJpKGdwc19jb3JwbG90JHIpXSkpW2MoJ3AudmFsdWUnKSxdKQogIGZjX2NvbV9ncHNfY29yID0gdW5saXN0KHNhcHBseShmY19jb21fY29yX3IsIGZ1bmN0aW9uKGF0bGFzKSBjb3IudGVzdChhdGxhcywgZ3BzX2NvcnBsb3Qkcltsb3dlci50cmkoZ3BzX2NvcnBsb3QkcildKSlbYygnZXN0aW1hdGUnKSxdKQogIGZjX2NvbV9ncHNfY29yX2RmID0gZGF0YV9mcmFtZShhdGxhcyA9IGdzdWIoIlxcLi4qIiwiIixuYW1lcyhmY19jb21fZ3BzX2NvcikpLCBjb3JyID0gZmNfY29tX2dwc19jb3IsIHBfdmFsID0gZmNfY29tX2dwc19wdmFsdWUgKQogIGZjX2NvbV9ncHNfY29yX2RmJHBfdmFsX2FkaiA9IHAuYWRqdXN0KGZjX2NvbV9ncHNfY29yX2RmJHBfdmFsLG1ldGhvZCA9ICJib25mZXJyb25pIikKICBmY19jb21fZ3BzX2Nvcl9kZiRzaWcgPSBmY19jb21fZ3BzX2Nvcl9kZiRwX3ZhbF9hZGo8IDAuMDUKICBwID0gZ2dwbG90KGRhdGE9ZmNfY29tX2dwc19jb3JfZGYsIGFlcyh4PXJlb3JkZXIoYXRsYXMsIC1jb3JyKSwgeT1jb3JyLCBmaWxsID0gc2lnKSkgKwogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArIHNjYWxlX3lfY29udGludW91cyhsaW1pdHM9YygtMC41LDAuNSkpICsKICAgIHRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKSArCiAgICB5bGFiKCJGQy1HUFMgU2ltaWxhcml0eSBNYXRyaWNlcyBDb3JyZWxhdG9ucyIpICsgeGxhYigiIikgKyBnZ3RpdGxlKGF0bGFzX25vdykgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKICByZXR1cm4obGlzdChmY19jb3JwbG90ID0gZmNfY29tX2NvcnBsb3QsIGZjX2dwc19kZiA9IGZjX2NvbV9ncHNfY29yX2RmLCBwbG90ID0gcCwgY29tX25hbWVzID0gY29tX25hbWVzKSkKfQpgYGAKCgoKYGBge3J9CmZjX3NjaGFlZjIwMF83X2NvbV9ncHMgPSBmY19jb21fZ3BzKHN1YmpfbmV0X2ZpbGVzXzEsc3Vial9uZXRfZmlsZXNfMiwic2NoYWVmZXIyMDB4NyIpCmZjX3Bvd2VyX2NvbV9ncHMgPSBmY19jb21fZ3BzKHN1YmpfbmV0X2ZpbGVzXzEsc3Vial9uZXRfZmlsZXNfMiwicG93ZXIyNjQiKQpmY19zY2hhZWY0MDBfN19jb21fZ3BzID0gZmNfY29tX2dwcyhzdWJqX25ldF9maWxlc18xLHN1YmpfbmV0X2ZpbGVzXzIsInNjaGFlZmVyNDAweDciKQpmY19zY2hhZWY0MDBfN19jb21fZ3BzJGZjX2NvcnBsb3QkYHZpcy1kb3JgJHBsb3QKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD01fQpmY19zY2hhZWYyMDBfN19jb21fZ3BzJHBsb3QgLyBmY19zY2hhZWY0MDBfN19jb21fZ3BzJHBsb3QgLyBmY19wb3dlcl9jb21fZ3BzJHBsb3QKYGBgCgpgYGB7ciBtYWtlIHRoaW5ncyB0byBtYXQsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTR9CmZjX2NvbV9ncHNfb3V0X2NvcnBsb3QgPSBmdW5jdGlvbihmY19jb21fZ3BzX291dCwgdGl0bGUpIHsKICB2ZWN0b3JfdG9fbWF0KGZjX2NvbV9ncHNfb3V0JGZjX2dwc19kZiRhdGxhcywiaGFsZitkIikKICBjb21fZ3BzX2NvcnJfbWF0ID0gdmVjdG9yX3RvX21hdChmY19jb21fZ3BzX291dCRmY19ncHNfZGYkY29yciwiaGFsZitkIikKICBjb21fbmFtZXMgPSBmY19jb21fZ3BzX291dCRjb21fbmFtZXMKICByb3duYW1lcyhjb21fZ3BzX2NvcnJfbWF0KSA9IGNvbV9uYW1lcwogIGNvbG5hbWVzKGNvbV9ncHNfY29ycl9tYXQpID0gY29tX25hbWVzCiAgY29tX2dwc19wdmFsX21hdCA9IHZlY3Rvcl90b19tYXQoZmNfY29tX2dwc19vdXQkZmNfZ3BzX2RmJHBfdmFsX2FkaiwiaGFsZitkIikKICBjb2wyIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiIzY3MDAxRiIsICIjQjIxODJCIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiNGRkZGRkYiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIzIxNjZBQyIsICIjMDUzMDYxIikpCmNvcnJwbG90KGNvbV9ncHNfY29ycl9tYXQsIHR5cGU9ImZ1bGwiLG1ldGhvZD0iY29sb3IiLCBjbC5saW0gPSBjKC0wLjIsMC4yKSwgdGwuY29sPSJibGFjayIsIAogICAgICAgICAgIHAubWF0ID0gY29tX2dwc19wdmFsX21hdCwgc2lnLmxldmVsID0gMC4wNSwgaW5zaWcgPSAiYmxhbmsiLCAKICAgICAgICAgICBjb2wgPSByZXYoY29sMigyMDApKSwgdGl0bGUgPSB0aXRsZSkKCn0KYGBgCgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIGZjX2NvbV9ncHNfb3V0X2NvcnBsb3QoZmNfc2NoYWVmMjAwXzdfY29tX2dwcywgInNjaGFlZjIwMCIpCgpgYGAKCmBgYHtyfQpmY19jb21fZ3BzX291dF9jb3JwbG90KGZjX3NjaGFlZjQwMF83X2NvbV9ncHMsICJzY2hhZWY0MDAiKSAKYGBgCgpgYGB7cn0KZmNfY29tX2dwc19vdXRfY29ycGxvdChmY19wb3dlcl9jb21fZ3BzLCAicG93ZXIyNjQiKQpgYGAKCiMjIyA3LiBNYXRjaCBGQyB0YXJnZXQgdG8gZGF0YWJhc2UKCmBgYHtyfQoKZ2V0X2FjY19maW5nZXIgPSBmdW5jdGlvbihzdWJqX25ldF9maWxlc18xLHN1YmpfbmV0X2ZpbGVzXzIpewogIGNvcl9kZiA9IGxpc3QoKQogIGF0bGFzZXMgPSBuYW1lcyhzdWJqX25ldF9maWxlc18xKQogIGFjYyA9IGFycmF5KCkKICBmb3IgKGF0bGFzIGluIGF0bGFzZXMpIHsKICAgIGNvcl9tYXRjaCA9IGxpc3QoKQogICAgc3Vial9uZXRfZmlsZXNfYXRsYXNfMSA9IHN1YmpfbmV0X2ZpbGVzXzFbW2F0bGFzXV0KICAgIHN1YmpfbmV0X2ZpbGVzX2F0bGFzXzIgPSBzdWJqX25ldF9maWxlc18yW1thdGxhc11dCiAgICBjb3JfbWF0Y2hbW2F0bGFzXV0gPSBsYXBwbHkoc3Vial9uZXRfZmlsZXNfYXRsYXNfMSwgZnVuY3Rpb24oc3Vial9jb3JfMSkgc2FwcGx5KHN1YmpfbmV0X2ZpbGVzX2F0bGFzXzIsIGZ1bmN0aW9uKHN1YmpfY29yXzIpIGNvcihzdWJqX2Nvcl8xJFYxLCBzdWJqX2Nvcl8yJFYxLCB1c2UgPSAibmEub3IuY29tcGxldGUiKSkpCiAgICBhY2NbYXRsYXNdID0gMAogICAgY29yX2xpc3QgPSBsaXN0KCkKICAgIGZvcihzdWJqIGluIG5hbWVzKGNvcl9tYXRjaFtbYXRsYXNdXSkpIHsKICAgICAgIHBvc2l0aW9uID0gd2hpY2gubWF4KHVubGlzdChjb3JfbWF0Y2hbW2F0bGFzXV1bW3N1YmpdXSkpCiAgICAgICBjb3JfbGlzdFtbYXRsYXNdXVtbc3Vial1dID0gbWF4KHVubGlzdChjb3JfbWF0Y2hbW2F0bGFzXV1bW3N1YmpdXSkpCiAgICAgICBwcmVkaWN0ZWRfc3ViaiA9IHN1YmplY3RzW3Bvc2l0aW9uXQogICAgICAgaWYgKHByZWRpY3RlZF9zdWJqID09IHN1YmopIHsKICAgICAgICAgICAgYWNjW2F0bGFzXT0gYWNjW2F0bGFzXSArIDEKICAgICAgICAgIH0KICAgIH0KICAgIAogICAgY29yX2RmW1thdGxhc11dID0gZGF0YS5mcmFtZShiYmxpZCA9IGFzLm51bWVyaWMobmFtZXMoY29yX2xpc3RbW2F0bGFzXV0pKSwgZmluZ2VyID0gdW5saXN0KGNvcl9saXN0W1thdGxhc11dKSkKICAgIGFjY1thdGxhc10gPSBhY2NbYXRsYXNdL2xlbmd0aChjb3JfbWF0Y2hbW2F0bGFzXV0pCiAgfQogICAgYWNjID0gYWNjWy0xXQogICAgcmV0dXJuKGxpc3QoY29yX2RmID0gY29yX2RmLCBhY2MgPSBhY2MpKQogIH0KCnNpbmdsZV9tdWx0aV9hY2MgPSBnZXRfYWNjX2ZpbmdlcihzdWJqX25ldF9maWxlc18xLHN1YmpfbmV0X2ZpbGVzXzIpCnNpbmdsZV9tdWx0aV9hY2NfZGYgPSBkYXRhLmZyYW1lKGF0bGFzID0gbmFtZXMoc2luZ2xlX211bHRpX2FjYyRhY2MpLCBhY2N1cmFjeSA9IHNpbmdsZV9tdWx0aV9hY2MkYWNjKQpwID0gZ2dwbG90KGRhdGE9c2luZ2xlX211bHRpX2FjY19kZiwgYWVzKHg9cmVvcmRlcihhdGxhcywgYWNjdXJhY3kpLCB5PWFjY3VyYWN5KSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogIHRoZW1lX2Nvd3Bsb3QoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMC41KSkgKwogIHlsYWIoIkZDLUdQUyBTaW1pbGFyaXR5IE1hdHJpY2VzIENvcnJlbGF0b25zIikgKyB4bGFiKCIiKQpwCgoKcHN5Y2hfc3VtX2ZpbmdlciA9IGlubmVyX2pvaW4oY29yX2RmLHBzeWNoX3N1bSwgYnkgPSBjKCdiYmxpZCcgPSAnQkJMSUQnKSkKCmZpdCA9IGxtKCBhY2MgfiBmaW5nZXIgKyAgc3VtX2FscyArIGRheXMsIGRhdGEgPSBwc3ljaF9zdW1fZmluZ2VyKQpzdW1tYXJ5KGZpdCkKYGBgCgoKIyMjIDguIFBlcm11dGF0aW9uIFRlc3QgZm9yIE1hdGNoaW5nCmBgYHtyfQpnZXRfc3Vial9uZXRfcGVybSA9IGZ1bmN0aW9uKHN1YmpfbmV0X2ZpbGVzKXsKICBzdWJqX25ldF9wZXJtID0gbGlzdCgpCiAgYXRsYXNlcyA9IG5hbWVzKHN1YmpfbmV0X2ZpbGVzKQogIGZvciAoYXRsYXMgaW4gYXRsYXNlcyl7CiAgICByYW5kb21fc2VxID0gc2FtcGxlKGxlbmd0aChzdWJqX25ldF9maWxlc1tbYXRsYXNdXSkpCiAgICBzdWJqX25ldF9wZXJtW1thdGxhc11dID0gc3Vial9uZXRfZmlsZXNbW2F0bGFzXV1bcmFuZG9tX3NlcV0KICAgIG5hbWVzKHN1YmpfbmV0X3Blcm1bW2F0bGFzXV0pID0gbmFtZXMoc3Vial9uZXRfZmlsZXNbW2F0bGFzXV0pCiAgfQogIHJldHVybihzdWJqX25ldF9wZXJtKQp9CgpzdWJqX25ldF9wZXJtXzEgPSBnZXRfc3Vial9uZXRfcGVybShzdWJqX25ldF9maWxlc18xKQpzdWJqX25ldF9wZXJtXzIgPSBnZXRfc3Vial9uZXRfcGVybShzdWJqX25ldF9maWxlc18yKQoKYGBgCgpgYGB7cn0KcGVybV90aW1lID0gMTAwMApzaW5nbGVfbXVsdGlfYWNjX3Blcm0gPSBsaXN0KCkKZm9yIChpIGluIDE6cGVybV90aW1lKXsKICBwcmludChpKQogIHN1YmpfbmV0X3Blcm1fMSA9IGdldF9zdWJqX25ldF9wZXJtKHN1YmpfbmV0X2ZpbGVzXzEpCiAgc3Vial9uZXRfcGVybV8yID0gZ2V0X3N1YmpfbmV0X3Blcm0oc3Vial9uZXRfZmlsZXNfMikKICBzaW5nbGVfbXVsdGlfYWNjX3Blcm1bW2ldXSA9IGdldF9hY2NfZmluZ2VyKHN1YmpfbmV0X3Blcm1fMSxzdWJqX25ldF9wZXJtXzIpCiAgcHJpbnQoc2luZ2xlX211bHRpX2FjY19wZXJtW1tpXV0kYWNjKQp9CgpgYGAKCmBgYHtyfQpwZXJtX2FjYyA9IGFzLmRhdGEuZnJhbWUodChzYXBwbHkoc2luZ2xlX211bHRpX2FjY19wZXJtLCBmdW5jdGlvbihwZXJtKSBwZXJtJGFjYykpKQpwZXJtX2FjY19sb25nIDwtIGdhdGhlcihwZXJtX2FjYywga2V5ID0gImF0bGFzIiwgdmFsdWUgPSAiYWNjdXJhY3kiLCBhbGxfb2YoYXRsYXNlcyksIGZhY3Rvcl9rZXk9VFJVRSkKcDwtZ2dwbG90KHBlcm1fYWNjX2xvbmcsIGFlcyh4PWF0bGFzLCB5PWFjY3VyYWN5LCBmaWxsID0gYXRsYXMpKSArIAogICBnZW9tX2ppdHRlcihzaG93LmxlZ2VuZCA9IEYpICsgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKC0wLjEsMC41KSkgKwogICBnZ3RpdGxlKCJQZXJtdXRhdGlvbiBUZXN0IGZvciBGQyBGaW5nZXJwcmludGluZyIpICsgdGhlbWVfY293cGxvdCgpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgdmp1c3QgPSAwLjUpKQpwCmBgYAoKYGBge3IgdXNlIG9ubHkgdGhlIEdQUy1GQyBzaWduaWZpY2FudCBuZXR3b3JrcyB0byBwcmVkaWN0IGluZGl2aWR1YWxzfQpnZXRfY29tX2ZjX2FjY3VyYWN5ID0gZnVuY3Rpb24oYXRsYXNfbm93KXsKICBmY19jb21fMSAgPSBnZXRfZmNfY29tKGF0bGFzX25vdywgc3Vial9uZXRfZmlsZXNfMSkKICBmY19jb21fMiAgPSBnZXRfZmNfY29tKGF0bGFzX25vdywgc3Vial9uZXRfZmlsZXNfMikKICAjZmNfY29tXzFfc2lnID0gZmNfY29tXzFbbmFtZXMod2hpY2goZmNfc2NoYWVmNDAwXzdfY29tX2dwcyRmY19ncHNfZGYkc2lnID09IFQpKV0KICAjZmNfY29tXzJfc2lnID0gZmNfY29tXzJbbmFtZXMod2hpY2goZmNfc2NoYWVmNDAwXzdfY29tX2dwcyRmY19ncHNfZGYkc2lnID09IFQpKV0KICBzaW5nbGVfbXVsdGlfYWNjX2NvbSA9IGdldF9hY2NfZmluZ2VyKGZjX2NvbV8xLGZjX2NvbV8yKQogIHNpbmdsZV9tdWx0aV9hY2NfY29tX2RmID0gZGF0YS5mcmFtZShhdGxhcyA9IG5hbWVzKHNpbmdsZV9tdWx0aV9hY2NfY29tJGFjYyksIGFjY3VyYWN5ID0gc2luZ2xlX211bHRpX2FjY19jb20kYWNjLCBGQ19HUFNfc2lnID0gZmNfc2NoYWVmNDAwXzdfY29tX2dwcyRmY19ncHNfZGYkc2lnID09IFQpCiAgcCA9IGdncGxvdChkYXRhPXNpbmdsZV9tdWx0aV9hY2NfY29tX2RmLCBhZXMoeD1yZW9yZGVyKGF0bGFzLCBhY2N1cmFjeSksIHk9YWNjdXJhY3ksIGZpbGwgPSBGQ19HUFNfc2lnKSkgKwogICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgICB0aGVtZV9jb3dwbG90KCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSkpICsKICAgIHlsYWIoIkZDIEZpbmdlcnByaW50IEFjY3VyYWN5IikgKyB4bGFiKCIiKQoKcmV0dXJuKGxpc3QoZGYgPSBzaW5nbGVfbXVsdGlfYWNjX2NvbV9kZiwgcGxvdCA9IHApKQp9CnM0MDBfZmNfY29tX2FjYyA9IGdldF9jb21fZmNfYWNjdXJhY3koInNjaGFlZmVyNDAweDciKQpzMjAwX2ZjX2NvbV9hY2MgPSBnZXRfY29tX2ZjX2FjY3VyYWN5KCJzY2hhZWZlcjIwMHg3IikKYGBgCgpgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD00fQoKczQwMF9mY19jb21fYWNjJHBsb3QgLyBzMjAwX2ZjX2NvbV9hY2MkcGxvdApgYGAKCgpgYGB7ciBpcnJpdGFiaWxpdHkgYW5kIHNwZWNpZmljIG5ldHdvcmtzfQpzNDAwX2ZjX2NvbV9hY2Nfc3Vic2V0ID0gc3Vic2V0KHM0MDBfZmNfY29tX2FjYyRkZiwgRkNfR1BTX3NpZyA9PSBUKQpzNDAwX2ZjX2NvbV8xICA9IGdldF9mY19jb20oInNjaGFlZmVyNDAweDciLCBzdWJqX25ldF9maWxlc18xKQpzNDAwX2ZjX2NvbV8yICA9IGdldF9mY19jb20oInNjaGFlZmVyNDAweDciLCBzdWJqX25ldF9maWxlc18yKQptb3Rpb25fZmlsZXNfMSA9IGxpc3QoKQptb3Rpb25fZmlsZXNfMiA9IGxpc3QoKQphdGxhc19ub3cgPSAic2NoYWVmZXI0MDB4NyIKc3ViamVjdHMgPSBsaXN0LmZpbGVzKGZpbGUucGF0aChkYXRhX3BhdGgsIi4uLyIsInF1YWxpdHlfY3N2IikpCmZvciAoc3ViaiBpbiBzdWJqZWN0cykgewogIHN1YmpfZmlsZXMgPSBsaXN0LmZpbGVzKGZpbGUucGF0aChkYXRhX3BhdGgsIi4uLyIsInF1YWxpdHlfY3N2Iiwgc3ViaikpCiAgbW90aW9uXzEgPSBwYXN0ZTAoIip0YXNrLXJlc3Qqc2luZ2xlKiIpCiAgbW90aW9uXzIgPSBwYXN0ZTAoIip0YXNrLXJlc3QqbXVsdGkqIikKICAKICBmaWxlX3BhdGhfMSA9IGZpbGUucGF0aChkYXRhX3BhdGgsICIuLi8iLCJxdWFsaXR5X2NzdiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3ViaixzdWJqX2ZpbGVzW2dyZXAoZ2xvYjJyeChtb3Rpb25fMSksc3Vial9maWxlcyldKQogIGZpbGVfcGF0aF8yID0gZmlsZS5wYXRoKGRhdGFfcGF0aCwgIi4uLyIsInF1YWxpdHlfY3N2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJqLHN1YmpfZmlsZXNbZ3JlcChnbG9iMnJ4KG1vdGlvbl8yKSxzdWJqX2ZpbGVzKV0pCiAgaWYgKGxlbmd0aChmaWxlX3BhdGhfMSk+MCkge21vdGlvbl9maWxlc18xW1tzdWJqXV0gPSByZWFkLnRhYmxlKGZpbGVfcGF0aF8xKSB9CiAgaWYgKGxlbmd0aChmaWxlX3BhdGhfMik+MCkge21vdGlvbl9maWxlc18yW1tzdWJqXV0gPSByZWFkLnRhYmxlKGZpbGVfcGF0aF8yKSB9Cn0KCgpnZXRfbmV0X3N1YmpfZGYgPSBmdW5jdGlvbihuZXQpIHsKICBuZXRfc3ViaiA9IHNhcHBseShuZXQsIGZ1bmN0aW9uKHN1YmopIG1lYW4oc3ViaiRWMSkpCiAgbmV0X3N1YmpfZGYgPSBkYXRhLmZyYW1lKHN1YmogPSBhcy5udW1lcmljKG5hbWVzKG5ldF9zdWJqKSksIG1lYW4gPSBuZXRfc3ViaikKICByZXR1cm4obmV0X3N1YmpfZGYpCn0KCnM0MDBfZmNfY29tX21lYW5fMSA9IGxhcHBseShzNDAwX2ZjX2NvbV8xW3M0MDBfZmNfY29tX2FjY19zdWJzZXQkYXRsYXNdLCBmdW5jdGlvbihuZXQpIGdldF9uZXRfc3Vial9kZihuZXQpKQpzNDAwX2ZjX2NvbV9tZWFuXzIgPSBsYXBwbHkoczQwMF9mY19jb21fMltzNDAwX2ZjX2NvbV9hY2Nfc3Vic2V0JGF0bGFzXSwgZnVuY3Rpb24obmV0KSBnZXRfbmV0X3N1YmpfZGYobmV0KSkKCnM0MDBfZmNfY29tX21lYW5fMV9ncHMgPSBsYXBwbHkoczQwMF9mY19jb21fbWVhbl8xLCBmdW5jdGlvbihuZXQpIGlubmVyX2pvaW4ocHN5Y2hfc3VtLCBuZXQsIGJ5ID0gYygiQkJMSUQiID0gInN1YmoiKSkpCgogICNmY19jb21fMV9zaWcgPSBmY19jb21fMVtuYW1lcyh3aGljaChmY19zY2hhZWY0MDBfN19jb21fIGdwcyRmY19ncHNfZGYkc2lnID09IFQpKV0KICAjZmNfY29tXzJfc2lnID0gZmNfY29tXzJbbmFtZXMod2hpY2goZmNfc2NoYWVmNDAwXzdfY29tX2dwcyRmY19ncHNfZGYkc2lnID09IFQpKV0KICBzaW5nbGVfbXVsdGlfYWNjX2NvbSA9IGdldF9hY2NfZmluZ2VyKGZjX2NvbV8xLGZjX2NvbV8yKQogIHNpbmdsZV9tdWx0aV9hY2NfY29tX2RmID0gZGF0YS5mcmFtZShhdGxhcyA9IG5hbWVzKHNpbmdsZV9tdWx0aV9hY2NfY29tJGFjYyksIGFjY3VyYWN5ID0gc2luZ2xlX211bHRpX2FjY19jb20kYWNjLCBGQ19HUFNfc2lnID0gCmBgYAoK
A work by Cedric Huchuan Xia
hxia@upenn.edu